this
的五种不同情形
默认情况
默认情况下,纯粹函数调用时,因为没有调用该方法的对象,故而此时的this
是undefined
,JavaScript 解释器会自动把这个对象指向全局对象 Global,在浏览器环境下,也即 window 对象。
1 2 3 4 5 6 7
| window.x = "Jackie";
function func() { console.log(this.x); }
func();
|
阮一峰老师的博客中提到:
在严格模式("use strict"
)下,会禁止this
指向全局对象,此时的this
会是undefined
。
事实上,ES5 的规范 中修改了描述,仅当非严格模式下才会有 this
指向的转变。所以此时this
无法改变指向,就会是undefined
,但并不是说无法指向全局对象。
1 2 3 4 5
| "use strict"; function foo() { console.log(this); } foo.call(this);
|
作为对象的方法调用
此时this
指向调用这个方法的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13
| var x = "Property of Window";
var obj = {}; obj.x = "Property of obj"; obj.f = function () { console.log(this.x); };
obj.f();
var f = obj.f; f();
|
call
、apply
和bind
的显式绑定
call
、apply
和bind
都可以改变一个函数的this
指向。
call
和apply
call
和apply
会将它们的调用对象的this
指向它们的第一个参数。
1 2 3 4 5 6 7 8 9 10 11
| function f() { console.log(this.x); }
var x = "Property of Window";
var obj = { x: "Property of obj", };
f.apply(obj);
|
当传入的第一个参数为 undefined,或者不传入参数时,在非严格模式下,自动会将this
指向全局对象 Global,在浏览器里是window
对象,严格模式下则会是undefined
:
1 2 3 4 5 6 7 8 9 10 11 12 13
| function f() { console.log(this); }
f.apply(); f.apply(undefined);
function ff() { "use strict"; console.log(this); } ff.apply(); ff.apply(undefined);
|
call
和apply
没有本质区别。唯一的区别在于:
call()
方法接受的是若干个参数的列表,而apply()
方法接受的是一个包含多个参数的数组。
bind
bind
和前面两者也并未有什么本质的区别,只不过bind
将第一个参数绑定当调用函数的this
上,并将这个函数返回(不执行)。
1 2 3 4 5 6 7 8 9 10 11 12
| function f() { console.log(this.x); }
var x = "Property of Window";
var obj = { x: "Property of obj", };
var ff = f.bind(obj); ff();
|
构造函数
当一个函数被当做构造函数,用new
关键字新建一个对象的时候,这个函数内部的this
以及原型链上的this
都会指向这个新建的对象。
1 2 3 4 5 6 7 8 9 10 11
| function Jackie(para) { this.para = para; console.log(this); } Jackie.prototype.log = function () { console.log(this); };
Jackie("hehe"); var p = new Jackie("haha"); p.log();
|
其他值得注意的绑定
放在超时代码里
JavaScript 中超时调用的代码,函数中 this 的值会指向 window 对象,在严格模式下也一样。因为超时调用的代码都会有一个隐式绑定:setTimeout(f, time) == setTimeout(f.bind(window), time)
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| "use stric"; var x = "Property of Window";
var obj = {}; obj.x = "Property of obj"; obj.ff = function () { setTimeout(function () { console.log(this.x); }, 100); };
obj.ff();
obj.fff = function () { var that = this; setTimeout(function () { console.log(that.x); }, 100); }; obj.fff();
|
事件监听函数中的 this
事件监听函数中的this
指向监听对象。
1 2 3 4 5 6
| var one = document.getElementById("one"); one.onclick = function () { console.log(this); };
one.click();
|
箭头函数
箭头函数中this
的指向,在函数定义时即绑定完毕,且后续无法更改。(即,会跟它的上级作用域的this
指针保持一致)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var obj = { x: 1 }
var f1 = () => { console.log(this) } f1.apply(obj) // Window
var f2 = function () { var f3 = () => { console.log(this) } return f3 }
var f4 = f2.apply(obj) f4() // Object {x: 1}
|
一个更神奇的例子,超时调用的代码在定义时,绑定了this
的指向。
1 2 3 4 5 6 7 8 9
| function foo() { setTimeout(() => { console.log("id:", this.id); }, 100); }
var id = 21;
foo.call({ id: 42 });
|
绑定的优先级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var obj = { x: 0, name: "obj" }; var robj = { x: -1, name: "robj" }; var factory = function (x) { this.x = x; console.log(this); };
var factoryBind = factory.bind(obj); robj.factory = factoryBind; robj.factory(2);
factoryBind.call(robj, 3); console.log(robj); console.log(obj);
var p = new factoryBind(4); console.log(p); console.log(obj);
|
可以见得,优先级从高到低:
new
,构造绑定
bind
,显式绑定
call
/apply
,显示绑定
- 作为方法绑定
- 默认绑定